#pragma once

#include "Schema.h"

Schema::Schema() {
};

void Schema::clear()
{
		numAttributes = 0;
		numRows = 0;
		attributeList.clear();
		attributeItems.clear();
		someMap.clear();
		attributeList.shrink_to_fit();
		attributeItems.shrink_to_fit();
};

Schema::Schema(int myNumAttributes, vector<NAME> myAttributeList) {
	numAttributes = myNumAttributes;
	attributeList = myAttributeList;
	for(int i=0;i<numAttributes;i++)
	{
		someMap.insert(std::make_pair<NAME, int>(attributeList.at(i), i));
	}
	numRows = 0;
};

void Schema::insertTuple(vector<INDEX> values) 
{
	vector<INDEX> lala;
	attributeItems.push_back(lala);
	for(int i=0;i<numAttributes;i++)
	{
		attributeItems.at(numRows).push_back(values.at(i));
	}
	++numRows;
};

vector<NAME> Schema::findCommonAttribute(Schema x) 
{
	vector<NAME> commonAttributeList;
	for(int i=0;i<numAttributes;i++)
	{
		for(int j=0;j<x.numAttributes;j++)
		{
			if((attributeList[i]==x.attributeList[j])&&(attributeList[i]!="_"))
			{
				commonAttributeList.push_back(attributeList[i]);
			}
		}
	}
	return commonAttributeList;
};

Schema Schema::naturalJoin(Schema x) 
{
	if(x.attributeList.size()==0)
	{
		vector<NAME> dummy;
		Schema ret(0,dummy);
		return ret;
	}
	else
	{
		vector<NAME> commonAttributeList = findCommonAttribute(x);
		if(commonAttributeList.size()==0)
		{
			vector<NAME> allAttVector;
			for(int k=0;k<(signed)attributeList.size();k++)
			{
				allAttVector.push_back(attributeList.at(k));
			}
			for(int k=0;k<(signed)x.attributeList.size();k++)
			{
				allAttVector.push_back(x.attributeList.at(k));
			}
			Schema returnScheme(allAttVector.size(),allAttVector);
		
			for(int i=0;i<numRows;i++)
			{
				for(int j=0;j<x.numRows;j++)
				{
					vector<INDEX> insertVector;
					for(int k=0;k<(signed)attributeList.size();k++)
					{
						insertVector.push_back(attributeItems.at(i).at(k));
					}
				
					for(int k=0;k<(signed)x.attributeList.size();k++)
					{
						insertVector.push_back(x.attributeItems.at(j).at(k));
					}
				
					returnScheme.insertTuple(insertVector);
				}
			}

			return returnScheme;
		}
		else
		{
			vector<NAME> extraAttributeList1;
			vector<NAME> extraAttributeList;

			for(int i=0;i<(signed)x.attributeList.size();i++)
			{
				for(int f=0;f<(signed)commonAttributeList.size();f++)
				{
					if(x.attributeList.at(i)!=commonAttributeList.at(f)&&x.attributeList.at(i)!="_")
					{
						extraAttributeList.push_back(x.attributeList.at(i));
					}
				}
			}
			for(int i=0;i<(signed)attributeList.size();i++)
			{
				for(int f=0;f<(signed)commonAttributeList.size();f++)
				{
					if(attributeList.at(i)!=commonAttributeList.at(f)&&attributeList.at(i)!="_")
					{
						extraAttributeList1.push_back(attributeList.at(i));
					}
				}
			}
			
			vector<NAME> combinedList;

			for(int i=0;i<(signed)commonAttributeList.size();i++)
			{
				combinedList.push_back(commonAttributeList.at(i));
			}

			for(int i=0;i<(signed)extraAttributeList.size();i++)
			{
				combinedList.push_back(extraAttributeList.at(i));
			}

			for(int i=0;i<(signed)extraAttributeList1.size();i++)
			{
				combinedList.push_back(extraAttributeList1.at(i));
			}

			Schema returnScheme(combinedList.size(),combinedList);
			
			for(int i=0;i<numRows;i++)
			{
				for(int j=0;j<x.numRows;j++)
				{
					bool localFlag = true;
					for(int k=0;k<(signed)commonAttributeList.size();k++)
					{
						if(attributeItems.at(i).at(someMap.at(commonAttributeList.at(k))) !=
							x.attributeItems.at(j).at(x.someMap.at(commonAttributeList.at(k))))
						{
							localFlag = false;
							break;
						}
					}
					if(localFlag)
					{
						vector<INDEX> insertVector;
						for(int f=0;f<(signed)commonAttributeList.size();f++)
						{
							insertVector.push_back(attributeItems.at(i).at(someMap.at(commonAttributeList.at(f))));
						}
						for(int f=0;f<(signed)extraAttributeList.size();f++)
						{
							insertVector.push_back(x.attributeItems.at(j).at(x.someMap.at(extraAttributeList.at(f))));
						}
						for(int f=0;f<(signed)extraAttributeList1.size();f++)
						{
							insertVector.push_back(attributeItems.at(i).at(someMap.at(extraAttributeList1.at(f))));
						}
						returnScheme.insertTuple(insertVector);
					}
				}
			}
			
			return returnScheme;
		}
	}
};

set<INDEX> Schema::project(NAME toProject)
{
	set<INDEX> returnVector;
	for(int f=0;f<(signed)attributeList.size();f++)
	{
		if(attributeList.at(f)==toProject)
		{
			for(int g=0;g<numRows;g++)
			{
				returnVector.insert(attributeItems.at(g).at(someMap.at(attributeList.at(f))));
			}
		}
	}
	return returnVector;
};

void Schema::printSchema()
{
	for(int i=0;i<numAttributes;i++)
	{
		cout << attributeList.at(i) << "|" ;
	}
	cout << "" << endl;
	for(int i=0;i<numRows;i++)
	{
		for(int j=0;j<(signed)attributeItems.at(i).size();j++)
		{
			cout << attributeItems.at(i).at(j) << "|" ;
		}
		cout << "" << endl;
	}
};

void Schema::deepCopy(Schema x)
{
	numAttributes = x.numAttributes;
	numRows = x.numRows;

	attributeList.clear();
	for(int i=0;i<x.numAttributes;i++)
	{
		attributeList.push_back(x.attributeList.at(i));
	}

	attributeItems.clear();
	for(int i=0;i<x.numRows;i++)
	{
		vector<INDEX> lala;
		attributeItems.push_back(lala);
	}
	for(int i=0;i<x.numRows;i++)
	{
		for(int j=0;j<(signed)x.attributeItems.at(i).size();j++)
		{
			attributeItems.at(i).push_back(x.attributeItems.at(i).at(j));
		}
	}

	someMap.clear();
	for(int i=0;i<x.numAttributes;i++)
	{
		someMap.insert(std::make_pair<NAME, int>(attributeList.at(i), x.someMap.at(attributeList.at(i))));
	}
};